package std

import (
	"context"
	"fmt"
	"go-rtc/common"
	"os"
	"unsafe"
)

const STD_BUFFER_SIZE = 4096

type StdinSource struct {
	ctx     context.Context
	outputs []*chan uintptr
}

func (source *StdinSource) Init(options common.Options) error {
	_ = options
	fmt.Println("Initialize Stdin Source....")
	return nil
}

func (source *StdinSource) SetOutputs(ch []*chan uintptr) {
	source.outputs = ch
}

func (source *StdinSource) AddOutput(ch chan uintptr) {
	if len(source.outputs) == 0 {
		source.outputs = make([]*chan uintptr, 1)
		source.outputs[0] = &ch
	} else {
		source.outputs = append(source.outputs, &ch)
	}
}

func (source *StdinSource) Read() error {
	for {
		select {
		case <-source.ctx.Done():
			return nil
		default:
			buffer := make([]byte, STD_BUFFER_SIZE)
			n, err := os.Stdin.Read(buffer)
			if err != nil {
				return err
			}
			if n > 0 && len(source.outputs) > 0 {
				buffer = buffer[:n]
				for _, output := range source.outputs {
					*output <- uintptr(unsafe.Pointer(&buffer[0]))
				}
			}
		}

	}

	return nil
}

func (source *StdinSource) Close() error {
	fmt.Println("Close Stdin Source")
	return nil
}

type StdoutSink struct {
	ctx    context.Context
	inputs []*chan uintptr
}

func (sink *StdoutSink) Init(options common.Options) error {
	_ = options
	fmt.Println("Initialize Stdout Sink....")
	return nil
}

func (sink *StdoutSink) SetInputs(ch []*chan uintptr) {
	sink.inputs = ch
}

func (sink *StdoutSink) AddInput(ch chan uintptr) {
	if len(sink.inputs) == 0 {
		sink.inputs = make([]*chan uintptr, 1)
		sink.inputs[0] = &ch
	} else {
		sink.inputs = append(sink.inputs, &ch)
	}
}

func (sink *StdoutSink) Write() error {
	for {
		select {
		case <-sink.ctx.Done():
			return nil
		default:
			if len(sink.inputs) > 0 {
				for _, input := range sink.inputs {
					p := <-*input
					bytes := (*[STD_BUFFER_SIZE]byte)(unsafe.Pointer(p))[:STD_BUFFER_SIZE:STD_BUFFER_SIZE]
					fmt.Printf("%s", string(bytes))
				}
			}
		}
	}

	return nil
}

func (sink *StdoutSink) Close() error {
	fmt.Println("Close Stdout Sink")
	return nil
}

func NewStdinSource(ctx context.Context) StdinSource {
	return StdinSource{
		ctx: ctx,
	}
}

func NewStdoutSink(ctx context.Context) StdoutSink {
	return StdoutSink{
		ctx: ctx,
	}
}
